`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          PORT_NUM                = 8;
localparam          WEIGHT_BW               = 4;        // weight bit width for single port
localparam          ALL_WEIGHT_BW           = PORT_NUM*WEIGHT_BW;
localparam          REQ_NUM                 = 10;

reg                                         rst_n;
reg                                         clk;

reg                 [PORT_NUM-1:0]          req;        // raw request vector
wire                                        req_exist;  // logic OR of all reqs whose cfg_weight>0

reg                                         sch_en;     // never set sch_en to 1 unless req_exist==1 
wire                [PORT_NUM-1:0]          gnt;        // immediate grants according to req and cnt_weight
wire                [PORT_NUM-1:0]          gnt_hld;    // locked grants at sch_en==1

reg                 [ALL_WEIGHT_BW-1:0]     cfg_weight;
reg                                         busy;

integer                                     cnt_sch[PORT_NUM-1:0];
integer                                     cnt_sch_all;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_arb_wrr", 1);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_REQ
    reg [PORT_NUM-1:0]  port;
    integer             gap;
    integer             i;
    integer             wght;

    cfg_weight = {4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd0};
    req = {PORT_NUM{1'b1}};

    wght = 0;
    for (i=0; i<PORT_NUM; i=i+1) begin
        wght = wght + cfg_weight[i*WEIGHT_BW+:WEIGHT_BW];
    end
    @(posedge rst_n);

    repeat(10*wght) begin
        @(posedge clk);

        while(sch_en==1'b0) begin
            @(posedge clk);
        end
    end
    req =`U_DLY 0;
    @(posedge clk);

    for (i=0; i<PORT_NUM; i=i+1) begin
        if (cnt_sch[i]!=cnt_sch_all*cfg_weight[i*WEIGHT_BW+:WEIGHT_BW]/wght) begin
            $error("cnt_sch[%0d]=%0d is wrong, %0d*%0d/%0d", i, cnt_sch[i], cnt_sch_all, cfg_weight[i*WEIGHT_BW+:WEIGHT_BW],wght);
            $stop;
        end
    end

    repeat(100) begin
        @(posedge clk);
    end

    gap  = $urandom_range(0, 10);
    port = $urandom();
    repeat(50000) begin
        @(posedge clk);

        for (i=0;i<PORT_NUM;i=i+1) begin
            if ((gap==0) && (port[i]==1'b1))
                req[i] =`U_DLY 1'b1;
            else if ((sch_en==1'b1) && (gnt[i]==1'b1)) 
                req[i] =`U_DLY 1'b0;
            else
                ;
        end

        if (gap==0) begin
            gap  = $urandom_range(0, 10);
            port = $urandom();
        end
        else
            gap  = gap -1;
    end

    rgrs.one_chk_done("sch num is done.");
end

initial begin:GEN_BUSY
    integer         rnd;

    busy = 1;

    @(posedge rst_n);
    @(posedge clk);

    forever begin
        @(posedge clk);
        rnd = $urandom_range(0, 9);
        busy =`U_DLY (rnd<8) ? 1'b0 : 1'b1;
    end
end

assign sch_en = req_exist & (~busy);
arb_wrr #(
        .PORT_NUM                       (PORT_NUM                       ),
        .WEIGHT_BW                      (WEIGHT_BW                      ) 	// weight bit width for single port
) u_arb_wrr ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .req                            (req                            ),	// raw request vector
        .req_exist                      (req_exist                      ),	// logic OR of all reqs whose cfg_weight>0

        .sch_en                         (sch_en                         ),	// never set sch_en to 1 unless req_exist
        .gnt                            (gnt                            ),	// immediate grants according to req and cnt_weight
        .gnt_hld                        (gnt_hld                        ),	// locked grants at sch_en

        .cfg_weight                     (cfg_weight                     )
);

genvar g0;
generate for (g0=0; g0<PORT_NUM; g0=g0+1) begin:G_CNT_SCH

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            cnt_sch[g0] <=`U_DLY 0;
        end else begin
            if ((sch_en==1'b1) && (gnt[g0]==1'b1))
                cnt_sch[g0] <=`U_DLY cnt_sch[g0] + 1;
            else
                ;
        end
    end
    
end endgenerate

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        cnt_sch_all <=`U_DLY 0;
    end else begin
        if (sch_en==1'b1)
            cnt_sch_all <=`U_DLY cnt_sch_all + 1;
        else
            ;
    end
end

endmodule

